#!/usr/bin/env python
#-*-coding: utf-8 -*-

# IPBeja - Escola Superior de Tecnologia e de Getão
# Engenharia Informática
# Elisio Goncalves Ribeiro nº4708
import sys
import xlrd
import csv
import os
import string
import operacoes_bd as BD
try:
    from unidecode import unidecode                                                 #retirado de http://pypi.python.org/pypi/Unidecode
except ImportError:
    print ' \nDescompacte o ficheio undecode.zip\n\
    para a mesma directoria do programa\n\n'
    sys.exit()
pass



##
###############################################################################
##
# metodo que verifica o nm_ficheiro fornecido tem a extenção correcta
# tenta abrir o nm_ficheiro e se conseguir tenta converter o 
#conteudo para UTF-8
def abrir_livro_ler_dados( nm_ficheiro, nome_bd):
    '''
    Abrir um nm_ficheiro do excel utilizando o 
    modulo de leitura de nm_ficheiros excel em python, xlrd
        
        nm_ficheiro - nome do nm_ficheiro a abrir (caminho relativo ou absoluto )
        nome_bd - nome e/ou endereço do nm_ficheiro base de dados one guardar
    '''        
    livro = None                                                               # uma referencia para guardar o documento fornecido
    if (not (string.lower(nm_ficheiro[-4:]) == '.xls')):                       #verificação se o nm_ficheiro tem a extensão certa
        nome_bd = ' ARQUIVO ERRADO!'
    else:
        try:                                                                   # em caso positivo
            livro = xlrd.open_workbook(nm_ficheiro, formatting_info=True)      #tenta abrir o documento para leitura
        except:
            nome_bd = " ERRO AO ABRIR '{0}'".format(nm_ficheiro)
        else:
            nome_bd = criarBD_guardar_dados(livro, nome_bd)                    #chamada da função das operações de crição da BDados
        pass
    pass
    return nome_bd
pass

##
###############################################################################
##
# metodo que recebe um endereco para a base de dados e os dados a guardar
# cria as tabelas apartir da lista de dados
# e salva para o disco
#
def criarBD_guardar_dados(livro_xls, endereco):
    '''
    Verifica o endereço fornecido, cria a base de dados e as tabelas
    e com o modulo bd_module guarda os dados no disco
        
        livro_xls - documento excel fornecido
        endereco - caminho completo ou nome da base de dados a criar
    '''
    conn,endereco = BD.sql_ligar_a_bd(endereco)                                    # criação da base de dados
    if (conn != None):
        cursor = conn.cursor()                                                     # criação do cursor
            
        for npag in range(2, livro_xls.nsheets):                                   # começar na pagina 2 
            pag_dados = livro_xls.sheet_by_index(npag)                             # para evitar introdução e indice
            
            cols_tabela = obter_campos_tabela(pag_dados)                           # cols_tabela com as colunas da tabela
            cols_tabela = unidecoding(cols_tabela)                                 #codificação unicode
            
            BD.sql_criar_tabelas(cursor, 
                                        cols_tabela[0], 
                                        cols_tabela[1:])                           # criacao da tabela
            conn.commit()
            
            pag_dados = obter_valores_folha(pag_dados)                             #obtenção dos valores a guardar na BDadoos
            unidecoding(pag_dados)                                                 # codificação do valores a guardar
            
            BD.sql_preencher_tabelas(cursor, 
                                            pag_dados, 
                                            cols_tabela[0], 
                                            cols_tabela[1:])                   # preenchimentos dos dados na BDados
            conn.commit()
        pass
        print '\n\n#####################################'
        BD.sql_guardar_desligar_bd(conn, cursor)                                # tudo lido e gurdado entao desligar
        
    else:
        #faz alguma coisacom a mensagem de erro
        pass
    pass
    return endereco
pass

##
###############################################################################
##
#metodo que faz a leitura dos dados em cada folha do documento
def obter_valores_folha(pagina):
    '''
    recebe uma folha de dados do excel, faz o tratamento e 
    retorna uma lista de tuplos com os dados para preencher tabelas
        
        pagina - folha de dados do documento fornecido
        return 'lst_final' - lista de tuplos com os dados lidos
    '''
    lst_final = []                                                           #lista definitiva
    cel_ant = ''
    lst_apoio = ['' for k in range(pagina.ncols)]
    if (pagina.cell_value(3,0) == ''):                                      # cabeçalhos com formatação multilinha ou
        i = 4
    else:
        i = 3                                                               # cabeçalho valor por linha
    pass
    
    #obter os restantes dos ddados das tabelas celula a celula
    for ln in xrange(i, pagina.nrows):
        tmp_dados = []                                                      #lista dos campos de uma linha
        col_ant = 'anos'        
        pos = 0
        for cl in xrange(pagina.ncols):
            cell_v = (ln, cl)
            
            if ((pagina.cell_value(2, cl) == '') and 
                        (pagina.cell_value(3, cl) == '')):
                pass
            elif (pagina.cell_value(3, cl) == 'HM'):                        # ignorar colunas com valor total HM
                pass
            else:
                ano_actual = pagina.cell_value(2, cl) 
                valor_actual = pagina.cell_value(ln, cl)
                
                if ((valor_actual == 'Total') or 
                    (valor_actual == 'total') or (valor_actual == 'HM')):
                    break                                                       # nao ler as linhas com valores totais
                if ((valor_actual == 'TOTAL') or (valor_actual == 'PORTUGAL')): 
                    break                                                       # indicação que chegou ao fim da tabela dos dados
                else:
                    if ano_actual == '': ano_actual = col_ant                   # se a celula estiver vazia é igual ao anterior
                    if (ano_actual[:3].isdigit()): 
                        if ((valor_actual == '-') or (valor_actual == '')):     #em casos de existir celulas numericas sem
                                                                                # valor ou marcadas com '-'
                            
                            valor_actual = 0.0                                  # substituir por 0.0 para fins de calculos
                        pass
                    if (isinstance(valor_actual, float)):                       # quando começar a ler os campos numericos
                        if (col_ant[:3].isdigit()):                             #se se trata do valor de um ano
                            if (col_ant == ano_actual):                         # e for igual ao ano da coluna actual
                                tmp_dados.append(valor_actual)                  # guarda o valor
                            else:                                               # ou
                                tmp_dados.append(col_ant)
                                lst_final.append(tuple(tmp_dados))              #guarda os dados agrupados na lista
                                
                                tmp_dados = tmp_dados[:cut_p]                   # a lista com os campos repetitivos
                                tmp_dados.append(valor_actual)                  # adiciona a ultima celula lida
                                col_ant = ano_actual                            # guarda o ultimo cabeçalho
                            pass
                        else:
                            cut_p = len(tmp_dados)                              #marcar o inicio daa leitura dos campos numericos
                            tmp_dados.append(valor_actual)
                            col_ant = ano_actual
                        pass
                    else:
                        if (valor_actual != ''):                                # quando lê um campo com informação
                            tmp_dados.append(valor_actual)                      # armazena  na lst temporaria
                            lst_apoio[pos] = valor_actual                       # e regista na lista de apoio
                            pos +=1
                        else:
                            valor_actual = lst_apoio[pos]                       # quando o campo estiver vazio, recorre 
                            tmp_dados.append(valor_actual)                      # a lista de apoio 
                            pos +=1
                        pass
                    pass                    
                pass
            pass                
        pass
        if ((valor_actual == 'TOTAL') or (valor_actual == 'PORTUGAL')):         #chegou ao fin dos dados relevantes, 
            break                                                               # termiina a leitura
        pass
    pass
    return lst_final
pass

##
###############################################################################
##
# metodo que retorna uma lista com o nome para a 
# tabela e os respectivos campos numa base de dados
def obter_campos_tabela(f):   
    '''
    recebe  uma pagina de dados excel 
    e retorna uma lista com o nome e os valores do cabeçalho 
    no formato (campo, tipo) pronto para operações sobre a base de dados
    
    f - folha de onde extrair os dados
    return 'colunas' - list object
    '''
    colunas = []
    colunas.append(f.name)                                              # formatano  
    colunas[0] = colunas[0].replace(' ', '_',1)                         # o nome
    colunas[0] = colunas[0].replace('.', '_',1)                         # da tabela
    colunas[0] = colunas[0].replace(' ', '',1)
    
    tipo_texto = 'VARCHAR(50)'                                          # tipo dos campos textos/strings
    tipo_num = 'CHAR(10)'                                               # tipos dos campos numericos

    for ln in range(2,4):
        val_linha = f.row_values(ln)
        
        for pos in xrange(len(val_linha)):                              # por cada coluna numa linha
            valor = val_linha[pos]
            v_ant = val_linha[pos -1]
            if (not (valor[:3].isdigit()) and (valor != '')
                                            and (valor != 'HM')):       # se nao for valor das colunas de anos
                valor = valor.strip()
                valor = valor.replace(' ', '_')                         # formata substituindo os espaços
                if (ln == 3):
                    valor = (valor, tipo_num)                           # adiciona o tipo numerico ou
                else:
                    valor = (valor, tipo_texto)                         #ou tipo texto
                pass
                if (valor not in colunas):
                    colunas.append(valor)                               # se aiinda nao existir na lista, adiciona
                pass
            elif(valor == ''):                                          # se encontrar coluna com valor mas sem cabeçalho
                tmp_v = v_ant[:v_ant.find(' ')]
                if (tmp_v == 'Subsistema'):                             # case de colunas merged mas que nao aparecemm 
                    valor = 'Tipo_' + v_ant[v_ant.rfind(' ')+1:]        # nas tabelas de merged_cell (bug?)
                    colunas.append((valor, tipo_texto))
                pass
            pass
        pass
        if (f.row(3)[0].value != ''):                                 # o primeiro valor da linha 3
            colunas.append(('Valor', tipo_num))
            break
        pass
    pass
    colunas.append(('Ano', tipo_texto))                                 # por fim adiciona o campo 'Ano'
    return colunas                                                      # e retorna uma lista
pass

##
###############################################################################
##
#codificando os dados fornecidos com unicode
def unidecoding(lista):
    '''recebe uma lista de tuplos e converte os campos strings 
        para unicode retirando as acentuações das palavras
        
        lista - conjunto de dados a converter
        return 'n_lista' - nova lista com os dados convertidos
    '''
    n_lista = []
    n_lista.append(lista[0])
    lista = lista[1:]
    for pos in xrange(len(lista)):
        tmp_lst = []
        for cmp in lista[pos]:
            
            if (isinstance(cmp, unicode)):
                tmp_lst.append(unidecode(cmp))
            else:
                tmp_lst.append(cmp)
            pass
        pass
        n_lista.append(tuple(tmp_lst))
    pass
    return n_lista
pass

#codificando os dados
def utf_8_encoder(lista):
    '''codifica a informação em utf8 e retorna-os numa lista'''
    r_lst = []
    for tp in lista:
        n_lst = []
        for vlw in tp: 
            n_lst.append(vlw.encode("utf8"))
        pass
        r_lst.append(tuple(n_lst))
    pass
    return r_lst
pass

##
# metodo para guardar os dados num ficheiro csv
def escrever_csv_ficheiro(nm_ficheiro, nms_colunas, lst_dados):
    ''' recebe o nome de um ficheiro e uma lista de dados em forma de tuplos
    e gurda esses dados num ficheiro com extensão .csv
        
        nm_ficheiro - nome do ficheiro CSV onde guardar os dados
        nms_colunas - nomes para cada coluna de dados
        lst_dados - lista de tuplos a escrever no ficheiro
    '''
    try:
        lst_dados = utf_8_encoder(lst_dados)
        nm_ficheiro = nm_ficheiro + ".csv"
        writer = csv.writer(open(nm_ficheiro, "wb"))
        writer.writerow(nms_colunas)
        writer.writerows(lst_dados)

    except: pass
pass


##
###############################################################################
##
if __name__ == "__main__":
    def usage():
        text = """
        usage:
            no ambiente python:
                lp1213-4708.py nF.xls cN_bd[.ext]
            
            nF.xls -> nome do nm_ficheiro no formato excel
            cN_bd[.ext] -> nome ou caminho (com ou sem extensão)
                        do nm_ficheiro para base de dados \n\n"""
        
        sys.stdout.write(text)
    pass
    
    if len(sys.argv) < 3:
        usage()
        sys.exit(0)
    else:
##  if len(sys.argv) == 3:
        print
        endereco = ''
##        endereco = abrir_livro_ler_dados(sys.argv[1], sys.argv[2])
        print '\nTerminada a criação da BDados\n'
##        BD.sql_pesquisa_cursos([sys.argv[3], sys.argv[4]])
        nc, ld = BD.sql_curso_por_nome(sys.argv[2], 'Gestão')
##        print ld
        escrever_csv_ficheiro(sys.argv[3],nc, ld)
    pass
    
pass    